/*! 
 * Header file for a drag force generator.
 *
 * \author Devis Rossini
 * \date 06 April 2012
 */ 
#ifndef PHXAERODRAGFORCEGENERATOR_H_INCLUDE_GUARD
#define PHXAERODRAGFORCEGENERATOR_H_INCLUDE_GUARD

#include "PhxCore.h"
#include "PhxForceGenerator.h"

#define VELOCITY_MAGNITUDE_THRESHOLD 0.1f

namespace phx
{
	/*!
     * A force generator that applies a drag force to a rigid body moving into a fluid. 
	 *
	 * \note This force generator can only be applied to a spehere.
     */
	class SphereAeroDrag : public ForceGenerator
	{
	public:

		/*! Creates the generator with the given parameters. */
		SphereAeroDrag(const real iFluidDensity, const real iSphereRadius, const real iDragCoefficient, 
			const Vector3& wind = Vector3::ZERO)
			:	mFluidDensity(iFluidDensity), 
				mSphereRadius(iSphereRadius), 
				mDragCoefficient(iDragCoefficient),
				mWind(wind) { }

		/*! Return the wind speed. */
		const Vector3& getWind() const
		{
			return mWind;
		}

		/*! Set the wind speed. */
		void setWind(const Vector3& iWind)
		{
			mWind = iWind;
		}

		/*! Applies the drag force to the given rigid body. */
		virtual void UpdateForce(RigidBody* oBody, real iDuration)
		{
			Vector3 bodyVelocity = oBody->GetVelocity();

			if (bodyVelocity.Magnitude() > VELOCITY_MAGNITUDE_THRESHOLD)
			{
				Vector3 apparentVelocity = bodyVelocity - mWind;
				real apparentVelocityMagnitude = apparentVelocity.Magnitude();

				real area = PI * mSphereRadius * mSphereRadius;

				// Fd = 1/2 * rho * A * Cd * va^2
				real aeroDragForce = 0.5f * mFluidDensity * area * mDragCoefficient * 
					apparentVelocityMagnitude * apparentVelocityMagnitude;

				real aeroDragForceX = 0.0f;
				real aeroDragForceY = 0.0f;
				real aeroDragForceZ = 0.0f;

				if (apparentVelocityMagnitude != 0)
				{
					aeroDragForceX = -aeroDragForce * apparentVelocity.x / apparentVelocityMagnitude;
					aeroDragForceY = -aeroDragForce * apparentVelocity.y / apparentVelocityMagnitude;
					aeroDragForceZ = -aeroDragForce * apparentVelocity.z / apparentVelocityMagnitude;	
				}

				Vector3 force(aeroDragForceX, aeroDragForceY, aeroDragForceZ);

				//if (force.Magnitude() < 0.1f)
				//	return;

				oBody->AddForce(force); 
			}
		}

	private:

		/*! Holds the fluid density. */
		real mFluidDensity;
		/*! Holds the sphere radius. */
		real mSphereRadius;
		/*! Holds the drag coefficient for the body. */
		real mDragCoefficient;

		/* Hold teh direction and the intensity of the wind. */
		Vector3 mWind;
	};
}

#endif // PHXAERODRAGFORCEGENERATOR_H_INCLUDE_GUARD